#include <sourcemod>
#include <sdktools>
#include <adminmenu>
#include <l4d_stocks>

#include "macros.sp"
#include "helpers.sp"

const TEAM_NONE = 0;

// SDK call handles
new Handle:gConf = INVALID_HANDLE;
new Handle:sdkSetPlayerSpec = INVALID_HANDLE;
new Handle:sdkTakeOverBot = INVALID_HANDLE;
new Handle:timer_afker = INVALID_HANDLE;

new bool:list_of_afk[MAXCLIENTS] = {false, ...};

new String:ProperTeamNames[][] = {
	"None",
	"Spectator",
	"Survivor",
	"Infected"
};


public OnPluginStart()
{
	gConf = LoadGameConfigFile("l4dteamspanel");
	if(gConf == INVALID_HANDLE)
	{
		ThrowError("Could not load gamedata/l4dteamspanel.txt");
	}

	StartPrepSDKCall(SDKCall_Player);
	PrepSDKCall_SetFromConf(gConf, SDKConf_Signature, "SetHumanSpec");
	PrepSDKCall_AddParameter(SDKType_CBasePlayer, SDKPass_Pointer);
	sdkSetPlayerSpec = EndPrepSDKCall();
	
	StartPrepSDKCall(SDKCall_Player);
	PrepSDKCall_SetFromConf(gConf, SDKConf_Signature, "TakeOverBot");
	PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_Plain);
	sdkTakeOverBot = EndPrepSDKCall();

	RegConsoleCmd("teams", show_team_menu);
	RegConsoleCmd("team", show_team_menu);
	RegConsoleCmd("afk", go_afk);
	RegConsoleCmd("away", go_afk);
	RegAdminCmd("spec", move_spec, ADMFLAG_KICK);

	HookEvent("round_start", Event_RoundStart);
	HookEvent("round_end", Event_RoundEnd);
	HookEvent("versus_round_start", Event_VersusRoundStart);
	HookEvent("player_disconnect", Event_PlayerDisconnect);
}

public Action:Event_PlayerDisconnect(Handle:event, const String:name[], bool:dontBroadcast)
{
	new id = GetClientOfUserId(GetEventInt(event, "userid"));
	remove_afker(id);
}

public Action:Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{
	timer_afker = CreateTimer(2.0, check_afker, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
}

public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
	if(timer_afker != INVALID_HANDLE)
	{
		KillTimer(timer_afker);
		timer_afker = INVALID_HANDLE;
	}
}

public Event_VersusRoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{

	if(timer_afker != INVALID_HANDLE)
	{
		KillTimer(timer_afker);
		timer_afker = INVALID_HANDLE;
	}

}

/* put back afker in the spec team*/
public Action:check_afker(Handle:Timer, any:junk)
{

	FOR_EACH_HUMAN(i)
	{
		if(IsValidClient(i) && GetClientTeam(i) != TEAM_SPECTATORS && was_afk(i))
		{
			PerformSwitch(i, TEAM_SPECTATORS);
		}
	}

}


public add_afker(client)
{
	if(!IsValidClient(client))
		return;

	list_of_afk[client] = true;
}

public remove_afker(client)
{
	if(!IsValidClient(client))
		return;

	list_of_afk[client] = false;
}

public bool:was_afk(client)
{
	// Check if player is still valid ...
	if (!IsValidClient(client))
	{
  		return false;
	}

	return list_of_afk[client];
}

public Action:move_spec(client, args)
{
	decl String:full[128];
	GetCmdArgString(full, sizeof(full));
	decl String:name[128];
	decl target_list[MAXCLIENTS], bool:tn_is_ml;

	if(IsValidClient(client))
	{
		
		new target_count = ProcessTargetString(
			full,
			client,
			target_list,
			MAXCLIENTS,
			COMMAND_FILTER_NO_BOTS,
			name,
			sizeof(name),
			tn_is_ml);
		
		if(!tn_is_ml && target_count == 1)
		{
			PerformSwitch(target_list[0], TEAM_SPECTATORS);
			PrintToChatAllExclude(target_list[0], -1, "\x03%N\x01 was moved to spectator.", target_list[0]);
			PrintToChat(target_list[0], "\x05You were moved to spectator by an admin.\x01");
		}
	}

	return Plugin_Handled;

}

public Action:go_afk(client, args)
{
	PerformSwitch(client, TEAM_SPECTATORS);
}

public Action:show_team_menu(client, args)
{

	new String:surv[24] = "Survivors ";
	new String:inf[24] = "Infected ";
	new String:spec[24] = "Spectators ";

	new String:surv_count[7];
	new String:inf_count[7];
	new String:spec_count[7];

	FormatEx(surv_count, 7, "(%d/%d)", get_total_client(TEAM_SURVIVORS), 10);
	FormatEx(inf_count, 7, "(%d/%d)", get_total_client(TEAM_INFECTED), 10);
	FormatEx(spec_count, 7, "(%d)", get_total_client(TEAM_SPECTATORS));

	StrCat(surv, 20, surv_count);
	StrCat(inf, 20, inf_count);
	StrCat(spec, 20, spec_count);

	new Handle:menu = CreateMenu(from_menu_choice);
	SetMenuTitle(menu, "Select a team :");
	AddMenuItem(menu,"2", surv);
	AddMenuItem(menu,"3", inf);
	AddMenuItem(menu,"1", spec);
	SetMenuExitButton(menu, true);
	DisplayMenu(menu, client, 20);
	
	return Plugin_Handled;
}


public from_menu_choice(Handle:menu, MenuAction:action, client, choice)
{
	if (action == MenuAction_Select)
	{
		new String:info[4];
		new bool:found = GetMenuItem(menu, choice, info, sizeof(info));
		if(found)
		{
			new team = StringToInt(info);
			PerformSwitch(client, team);
		}
	}
	else if (action == MenuAction_Cancel) 
	{
		PrintToServer("Client %d's menu was cancelled.  Reason: %d", client, choice);
	}
	else if (action == MenuAction_End)
	{
		CloseHandle(menu);
	}
}

public get_total_client(team)
{
	new count = 0;
	FOR_EACH_HUMAN(client)
	{
		if(GetClientTeam(client) == team)
			count++;
	}

	return count;
}


PerformSwitch(target, team) {

	// Check if player is still valid ...
	if (!IsValidClient(target))
	{
		return false;
	}
	
	// If teams are the same ...
	if (GetClientTeam(target) == team)
	{
		return false;
	}
	
	// We check if target team is full...
	if (IsTeamFull(team))
	{	
		PrintToChat(target, "\x05%s\x01 team is full", ProperTeamNames[team]);
		return false;
	}
	
	// If player was on infected .... 
	if (GetClientTeam(target) == TEAM_INFECTED)
	{		
		// ... and he wasn't a tank nor ghost...
		new String:iClass[100];
		
		GetClientModel(target, iClass, sizeof(iClass));
		
		if (StrContains(iClass, "hulk", false) == -1 && !L4D_IsPlayerGhost(target)) 
		{ 
			ForcePlayerSuicide(target); 
		}
	}
	
	// If target is survivors
	if (team == TEAM_SURVIVORS)
	{

		//remove from afk list
		remove_afker(target);

		// first we switch to spectators ..
		ChangeClientTeam(target, TEAM_SPECTATORS); 
		
		FOR_EACH_ALIVE_SURVIVOR_BOT(bot)
		{
			// force player to spec humans
			SDKCall(sdkSetPlayerSpec, bot, target); 
		
			// force player to take over bot
			SDKCall(sdkTakeOverBot, target, true); 
			PrintToChatAll("\x03%N\x01 joined the \x03%s\x01 team!", target, ProperTeamNames[team]);
			return true;
		}

		//if don't find alive, take a dead one
		FOR_EACH_DEAD_SURVIVOR_BOT(bot)
		{
			// force player to spec humans
			SDKCall(sdkSetPlayerSpec, bot, target); 
		
			// force player to take over bot
			SDKCall(sdkTakeOverBot, target, true); 
			PrintToChatAll("\x03%N\x01 joined the \x03%s\x01 team!", target, ProperTeamNames[team]);
			return true;
		}
		
	}
	else if(team == TEAM_INFECTED)
	{
		//remove from afk list
		remove_afker(target);
		ChangeClientTeam(target, team);
		PrintToChatAll("\x03%N\x01 joined the \x03%s\x01 team!", target, ProperTeamNames[team]);
	}
	else // spectator
	{
		add_afker(target);
		ChangeClientTeam(target, team);
	}
		
	return true;
	
}


stock bool:IsTeamFull(team) {

	
	if (team == TEAM_SPECTATORS) { return false; }
	
	new count = get_total_client(team);
	
	if (team == TEAM_INFECTED) 
	{ 	
		return (count >= MAXINFECTED);
	}
	else
	{
		return (count >= MAXSURVIVORS);
	}
	
}
